﻿//Copyright (C) 2009  Jaco (ScionBot.com)

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Scion.Library.Underlying;
using System.Drawing;
using Scion.Library.Antiban;

namespace Scion.Library.Advanced
{
    /// <summary>
    /// This class finds text on the screen.
    /// </summary>
    public class OCR : Determine
    {
        /// <summary>
        /// Finds text.
        /// </summary>
        /// <param name="canvas">The bitmap to find the text in.</param>
        /// <param name="rect">The bounds to search in.</param>
        /// <param name="text">The text to search for.</param>
        /// <param name="f">The type of font to search for.</param>
        /// <param name="similarColours">The colour of the text to search for.</param>
        /// <param name="spaceTolerance">The tolerance of space between each character.</param>
        /// <param name="tolerance">The pixel tolerance.</param>
        /// <param name="strict">Use strict text finding (true).</param>
        /// <returns>Returns the point the text was found at.</returns>
        public static Point FindText(BufferedImage canvas, Rectangle rect, string text, CharFont f, Color[] similarColours,  int spaceTolerance, int tolerance, bool strict)
        {
            List<BitmapChar> bitmapChars = GetCharList(f);
            List<CharPoint> pointDump = new List<CharPoint>();
            canvas = ApplyFilter(rect, similarColours, tolerance, canvas);
            Point returnPoint = new Point(-1, -1);
            int previousCharIndex = 0;
            for (int p = 0; p < text.Length; p++)
            {
                Char chr = text[p];
                int c;
                for (c = 0; c < bitmapChars.Count; c++)
                {
                    BitmapChar bitmapChar = bitmapChars[c];
                    if (bitmapChar.bmpKey == (int)chr)
                    {
                        pointDump.Add(new CharPoint(chr, Cluster.FindBitmapAdvanced(canvas, bitmapChar.bmpChar, rect, Color.Black, strict)));
                        if (p == 0 && (pointDump[0].points == null))
                            return new Point(-1, -1);
                        else if (p == 0)
                            goto NextChar;
                        else if (p > 0)
                        {
                            List<Point> previous = pointDump[p - 1].points;
                            List<Point> current = pointDump[p].points;
                            if (previous == null || current == null)
                                return new Point(-1, -1);
                            for (int pr = 0; pr < previous.Count; pr++)
                            {
                                for (int cr = 0; cr < current.Count; cr++)
                                {
                                    if (IsWithinBounds(previous[pr].Y, current[cr].Y, 10))
                                    {
                                        int compareX = current[cr].X - bitmapChars[previousCharIndex].bmpChar.imageWidth;
                                        if (IsWithinBounds(compareX, previous[pr].X, spaceTolerance))
                                        {
                                            if ((p - 1) == 0)
                                                returnPoint = previous[pr];
                                            goto NextChar;
                                        }
                                    }
                                }
                            }
                            return new Point(-1, -1);
                        }
                    }
                }
            NextChar:
                previousCharIndex = c;
                continue;
            }
            return returnPoint;
        }

        /// <summary>
        /// Finds text and returns the centre point of the text found.
        /// </summary>
        /// <param name="canvas">The bitmap to find the text in.</param>
        /// <param name="rect">The bounds to search in.</param>
        /// <param name="text">The text to search for.</param>
        /// <param name="f">The type of font to search for.</param>
        /// <param name="similarColours">The colour of the text to search for.</param>
        /// <param name="spaceTolerance">The tolerance of space between each character.</param>
        /// <param name="tolerance">The pixel tolerance.</param>
        /// <param name="strict">Use strict text finding (true).</param>
        /// <returns>Returns the centre point of the text found.</returns>
        public static PointRandom ClickText(BufferedImage canvas, Rectangle rect, string text, CharFont f, Color[] similarColours,  int spaceTolerance, int tolerance, bool strict)
        {
            List<BitmapChar> bitmapChars = GetCharList(f);
            Point r = FindText(canvas, rect, text, f, similarColours, spaceTolerance, tolerance, strict);
            if (r.X == -1 && r.Y == -1)
                return new PointRandom(-1, -1, 0);
            int height = bitmapChars[0].bmpChar.imageHeight;
            int width = 0;
            foreach (char c in text)
            {
                foreach (BitmapChar bc in bitmapChars)
                {
                    if (Convert.ToChar(bc.bmpKey) == c)
                    {
                        width += bc.bmpChar.imageWidth;
                        break;
                    }
                }
            }
            if (width == 0)
                return new PointRandom(-1, -1, 0);
            SizeRandom sr = new SizeRandom(new Size(width, height), r);
            sr.random.Y = (height / 2) + r.Y + new Random().Next(-2, 2);
            return new PointRandom(sr.random.X + 7, sr.random.Y, 0);
        }

        /// <summary>
        /// Returns a string of the text found in the canvas.
        /// </summary>
        /// <param name="canvas">The image to find the text in.</param>
        /// <param name="rect">The bounds to search for the text in.</param>
        /// <param name="f">The font of the text.</param>
        /// <param name="similarColours">The colours of the text.</param>
        /// <param name="tolerance">The tolerance of each pixel checked against.</param>
        /// <param name="basic">If true, the result returned will only contain numbers and letters.</param>
        /// <returns>Returns a string of characters the function found within the bounds.</returns>
        public static string GetText(BufferedImage canvas, Rectangle rect, CharFont f, Color[] similarColours, int tolerance, bool basic)
        {
            List<BitmapChar> bitmapChars = GetCharList(f);
            List<AdvancedPoint> foundDump = new List<AdvancedPoint>();
            canvas = ApplyFilter(rect, similarColours, tolerance, canvas);
            for (int i = 0; i < bitmapChars.Count; i++)
            {
                BitmapChar bitmapChar = bitmapChars[i];
                if (basic)
                {
                    String str = Convert.ToChar(bitmapChar.bmpKey).ToString().ToLower();
                    String str2 = str.ToUpper();
                    if (str != str2 || bitmapChar.bmpKey == 32 || (bitmapChar.bmpKey > 47 && bitmapChar.bmpKey < 58))
                    {
                        List<AdvancedPoint> points = Cluster.FindBitmapAdvanced(canvas, bitmapChar.bmpChar, Convert.ToChar(bitmapChar.bmpKey), rect, Color.Black, true);
                        if (points != null)
                            foundDump.AddRange(points);
                    }
                }
                else
                {
                    List<AdvancedPoint> points = Cluster.FindBitmapAdvanced(canvas, bitmapChar.bmpChar, Convert.ToChar(bitmapChar.bmpKey), rect, Color.Black, true);
                    if (points != null)
                        foundDump.AddRange(points);
                }
            }
            foundDump.Sort(new PointSort(PointSort.Mode.X));
            StringBuilder sb = new StringBuilder();
            for (int s = 0; s < foundDump.Count; s++)
                sb.Append(foundDump[s].chr);
            return sb.ToString();
        }

        private static BufferedImage ApplyFilter(Rectangle rect, Color[] similarColours, int tolerance, BufferedImage canvas)
        {
            for (int Y = rect.Y; Y < rect.Height; Y++)
            {
                for (int X = rect.X; X < rect.Width; X++)
                {
                    int canvasR; int canvasG; int canvasB;
                    canvas.GetPixel(X, Y, out canvasR, out canvasG, out canvasB);
                    for (int i = 0; i < similarColours.Length; i++)
                    {
                        int simR = similarColours[i].R; int simG = similarColours[i].G; int simB = similarColours[i].B;
                        if (IsTolerance(canvasR, canvasG, canvasB, simR, simG, simB, tolerance))
                        {
                            canvas.SetPixel(X, Y, 255, 255, 255);
                            break;
                        }
                    }
                }
            }
            return canvas;
        }

        private static List<BitmapChar> GetCharList(CharFont f)
        {
            switch (f)
            {
                case CharFont.CharsNPC:
                    return Fonts.CharsNPC;
                case CharFont.CharsTrade:
                    return Fonts.CharsTrade;
                case CharFont.SmallChars:
                    return Fonts.SmallChars;
                case CharFont.StatChars:
                    return Fonts.StatChars;
                case CharFont.UpChars:
                    return Fonts.UpChars;
            }
            return null;
        }
    }

    /// <summary>
    /// The fonts available for the OCR to search for.
    /// </summary>
    public enum CharFont
    {
        CharsNPC = 1,
        CharsTrade = 2,
        SmallChars = 3,
        StatChars = 4,
        UpChars = 5,
    }
}
